#! /usr/bin/perl

#-------------------------------------------------------------------------------
#--
#-- Module      :  Main
#-- Copyright   :  (c) Lakshmi Narasimhan T V
#-- License     :  GNU GPLv3
#--
#-- Maintainer  :  <lakshminaras2002 at gmail dot com>
#-- Stability   :  provisional
#-- Portability :  portable
#--
#--
#--
#---------------------------------------------------------------------------------

# This script builds haskell related packages in Fedora rawhide.
# Default is to build all packages
# Use -b option to specify a single package to build

use strict;
use warnings;
use Getopt::Long;
use File::Basename;
use Cwd 'abs_path';
use POSIX;

my $PROG;
my $PROG_DIR;

BEGIN
{
$PROG = File::Basename::basename($0);
my $TEMP_DIR = File::Basename::dirname($0);
$PROG_DIR = abs_path($TEMP_DIR);
push @INC,$PROG_DIR;
}

use Utils;
use DepConstructor;
use PackageRebuilder;
use OptionsReader;

my %pkg_dep_map = ();
my @exclude_list = ("ghc");
# Value is either 1 or 0
# 0 is built -1 is failure
my %built = ();

# Command line variables
my $shell_script = '';
my $commit_msg = '';
my $help = 0;
my $verbose = 0;
my $printdeps = '';
my $list = 0;
my $package_to_build = '';
my $reusedirs = 0;

# All prototypes
sub display_help_message();
sub build_package($);

sub display_help_message()
{
print<<EOF;
$PROG

Usage:
--------

-h|-H|--help             - Displays this help message 
-v|-V|--verbose          - Be verbose
-l|-L|--list             - List packages under consideration
-u|-U|--reuse            - Reuse cloned directories at scratch directory. This is useful if you do not want to clone directories again and again.
-p <packagename>         - Prints build dependency for a package. Use <packagename> as all to print dependency information for all packages
-b <packagename>         - Builds package <package>. Requires -s and -m options to be passed
-s <script>              - The path of shell script to apply on the spec file
-m <message>             - Commit message for this rebuild
EOF
}

# For each package,
# # If built?
# # # Return built status
# # No deps
# # # Build this package, add to built map and return success or failure
# # For each dep
# # # Check if it is built and whether it is success or failure
# # # If not in built, call package_builder for this dep
# # # Depending on the return value
# # # # If success ,proceed to next dep
# # # # If failure, cannot do anthing to this package
# # If all deps were successful, build this package. 
# # # Before proceeding, add this package to the built list.

sub build_package($)
{
  my $pkg_name = shift;
  
  print_v("-----------------------------Building package $pkg_name-----------------------------");
  if(is_built($pkg_name))
  {
    my $build_result = $built{$pkg_name};
    print_v("Not building package package $pkg_name. Is built with status $build_result");
    return $build_result;
  }
  
  # Very much possible that we may not have the pkgname in the map
  # Just return 1 in these cases
  if(!($pkg_name ~~ %pkg_dep_map))
  {
    print_v("Package $pkg_name is not in map. Currenly assuming that it is built");
    return 1;
  }

  my $specfile_path = get_specfilepath($pkg_name);
  my @deps = get_deps($pkg_name);
  
  my $build_result = 0;

  # No deps
  if(length @deps == 0)
  {
    print_v("No deps ..building $pkg_name");
    $build_result = do_build($pkg_name, $specfile_path);
  }
  else
  {
    my $deps_built = 0;
    foreach my $dep (@deps)
    {
      my $dep_build_result = 0;
      if(is_built($dep))
      {
        $dep_build_result = $built{$dep};
        print_v("Not building $dep. Built with status $dep_build_result...");  
      }
      else
      {
        $dep_build_result = build_package($dep);
      }
      if($dep_build_result == 1)
      {
        $deps_built = 1;
        next;
      }
      else
      {
          # Short circuit
        $deps_built = 0;
        print_v("Failed to build $dep");
        last;
      }
    }
    if($deps_built == 1)
    {
      $build_result = do_build($pkg_name, $specfile_path);
    }    
  }
  $built{$pkg_name} = $build_result;
  print_v("----------------------Finished building package $pkg_name-----------------------------");
  return $build_result;
}

sub is_built($)
{
  my $pkg_name = shift;
  if(defined $built{$pkg_name})
  {
    return 1;
  }
}
sub get_deps($)
{
  my @deps = ();
  my $pkg_name = shift;
  my @pair = @{$pkg_dep_map{$pkg_name}};
  if(@pair)
  {
    @deps = @{$pair[1]};
  }
  return @deps;
}

sub get_specfilepath($)
{
  my $specfile_path = "";
  my $pkg_name = shift;
  my @pair = @{$pkg_dep_map{$pkg_name}};
  if(@pair)
  {
    $specfile_path = ${$pair[0]};
  }
  return $specfile_path;
}

sub do_build($$)
{ 
  my $pkg_name = shift;
  my $specfile_path = shift;
  if(!(defined $specfile_path) || ($specfile_path eq ''))
  {
    # Assume that these are built already
    print_v("Skipping $pkg_name");
    return 1;
  }
  return run_build_script($specfile_path, $shell_script, $commit_msg);
}

sub print_deps($)
{
  my $pkg_name = shift;
  my @deps = get_deps($pkg_name);
  print "$pkg_name : ";
  foreach my $dep (@deps)
  {
    print " $dep";
  }
}

sub main()
{
  if($#ARGV <0)
  {
    display_help_message();
    exit_failure("No command line argument was passed.");   
  }
  
  # TODO We could use an options map and pass it around.
  if(GetOptions (
  # Mode options
	'h|H|help'                 => \$help,
	'v|V|verbose'             => \$verbose,
	'l|L|list'             => \$list,
        'u|U|reuse'            => \$reusedirs,
	'p=s'           => \$printdeps,
	's=s'                       => \$shell_script,
	'm=s'                       => \$commit_msg,
	'b=s'                      => \$package_to_build
  ) == 0)
  {
    display_help_message();
    exit_failure("Error processing command line arguments.");
  }

  if($help)
  {
    display_help_message();
    exit_success();
  }
  
  if($list)
  { 
    my @pkgs = get_package_list();
    foreach my $pkg_name (@pkgs)
    {
      print_v($pkg_name);
    }
    exit_success();
  }

  %pkg_dep_map = dep_constructor($reusedirs);
  
  if($package_to_build ne '')
  {
    
    if($shell_script eq '' || $commit_msg eq '')
    {  
       display_help_message();
       exit_failure("Invalid number of arguments. To build a package, pass -s and -m options also");
    }
    build_package($package_to_build);
    exit_success();
  }

   
  if(($printdeps ne 'all') && ($printdeps ne ''))
  {
     print_deps($printdeps);
     exit_success();
  }

  foreach my $pkg_name (keys %pkg_dep_map)
  {
    if($printdeps eq 'all')
    { 
      print_deps($pkg_name);
      print_v("");
      next;
    }
    if($pkg_name ~~ @exclude_list)
    {
      next;
    }
    build_package($pkg_name);
  }
}

main();
